home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Frameworks / Hsoi's App Shell 1.0a4 / HAS Other Source / WASTE 1.3a4 Distribution / WASTE 1.3a4 / Source / WEMouse.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-04-04  |  36.5 KB  |  1,568 lines  |  [TEXT/CWIE]

  1. /*
  2.  *    WEMouse.c
  3.  *
  4.  *    WASTE PROJECT
  5.  *  Mouse Clicks and Support for Drag and Drop
  6.  *
  7.  *  Copyright (c) 1993-1997 Marco Piovanelli
  8.  *    All Rights Reserved
  9.  *
  10.  *  C port by Dan Crevier
  11.  *
  12.  */
  13.  
  14.  
  15. #include "WASTEIntf.h"
  16.  
  17. #ifndef __FOLDERS__
  18. #include <Folders.h>
  19. #endif
  20.  
  21. #if WASTE_IC_SUPPORT
  22. #ifndef __ICTYPES__
  23. #include "ICTypes.h"
  24. #endif
  25. #ifndef __ICAPI__
  26. #include "ICAPI.h"
  27. #endif
  28. #endif // WASTE_IC_SUPPORT
  29.  
  30. //    static variables
  31.  
  32. static DragSendDataUPP _weFlavorSender = nil ;
  33.  
  34. pascal Boolean _WEIsOptionDrag ( DragReference drag )
  35. {
  36.     EventModifiers downModifiers, upModifiers ;
  37.  
  38.     // get drag modifiers
  39.     // ??? shouldn't GetDragModifiers take EventModifiers* parameters ???
  40.     GetDragModifiers ( drag, nil, ( SInt16 * ) & downModifiers, ( SInt16 * ) & upModifiers ) ;
  41.  
  42.     // return true if the option key was held down at the beginning and/or at the end
  43.     return ( ( ( downModifiers | upModifiers ) & optionKey ) != 0 ) ;
  44. }
  45.  
  46. pascal OSErr _WEGetFlavor ( DragReference drag, ItemReference dragItem,
  47.                 FlavorType requestedType, Handle hFlavor,
  48.                 SInt32 dropOffset, WEHandle hWE )
  49. {
  50.     // get the requested flavor out of the specified drag reference and put it into
  51.     // the given handle, if any -- if hFlavor is nil, just check whether the specified flavor
  52.     // is there or can be obtained by invoking a user-defined translation routine
  53.  
  54.     FlavorFlags flags ;
  55.     Size dataSize ;
  56.     WETranslateDragUPP translateDragHook ;
  57.     Boolean saveFlavorLock ;
  58.     OSErr err ;
  59.  
  60.     // see if the drag item has the requested flavor type,
  61.     // without forcing the actual data to be sent and/or translated
  62.     err = GetFlavorFlags ( drag, dragItem, requestedType, & flags ) ;
  63.     if ( err == badDragFlavorErr )
  64.     {
  65.         // requested flavor is not available: our client may try a custom translation
  66.         // this is especially handy to translate HFS objects like TEXT and PICT files
  67.         if ( ( translateDragHook = ( * hWE ) -> translateDragHook ) != nil )
  68.         {
  69.             err = CallWETranslateDragProc ( drag, dragItem, requestedType, hFlavor,
  70.                         dropOffset, hWE, translateDragHook ) ;
  71.         }
  72.     }
  73.     else if ( err == noErr )
  74.     {
  75.  
  76.         // requested flavor is available: get it if hFlavor is not nil
  77.         if ( hFlavor == nil )
  78.         {
  79.             return err ;
  80.         }
  81.  
  82.         // get size of flavor data
  83.         if ( ( err = GetFlavorDataSize( drag, dragItem, requestedType, & dataSize ) ) != noErr )
  84.         {
  85.             return err ;
  86.         }
  87.  
  88.         // resize the handle
  89.         SetHandleSize ( hFlavor, dataSize ) ;
  90.         if ( ( err = MemError ( ) ) != noErr )
  91.         {
  92.             return err ;
  93.         }
  94.  
  95.         // get flavor data
  96.         saveFlavorLock = _WESetHandleLock ( hFlavor, true ) ;
  97.         err = GetFlavorData ( drag, dragItem, requestedType, * hFlavor, & dataSize, 0 ) ;
  98.         _WESetHandleLock ( hFlavor, saveFlavorLock ) ;
  99.     }
  100.  
  101.     return err;
  102. }
  103.  
  104. pascal OSErr _WEExtractFlavor ( DragReference drag, ItemReference dragItem,
  105.                         FlavorType theType, Handle *hFlavor,
  106.                         SInt32 dropOffset, WEHandle hWE )
  107. {
  108.     OSErr err;
  109.  
  110.     // allocate a new handle
  111.     if ( ( err = _WEAllocate ( 0, kAllocTemp, hFlavor ) ) == noErr )
  112.     {
  113.         // put the requested flavor into this handle
  114.         if ( ( err = _WEGetFlavor ( drag, dragItem, theType, * hFlavor, dropOffset, hWE ) ) != noErr )
  115.         {
  116.  
  117.         // if an error occurred, forget the handle
  118.             _WEForgetHandle ( hFlavor ) ;
  119.         }
  120.     }
  121.  
  122.     return err ;
  123. }
  124.  
  125. pascal Boolean WECanAcceptDrag ( DragReference drag, WEHandle hWE )
  126. {
  127.     WEPtr pWE ;
  128.     UInt16 numDragItems ;
  129.     UInt16 dragItemIndex ;
  130.     ItemReference dragItem ;
  131.     Boolean saveWELock ;
  132.     OSErr err ;
  133.     Boolean retval = false ;
  134.  
  135.     // lock the WE record
  136.     saveWELock = _WESetHandleLock ( ( Handle ) hWE, true ) ;
  137.     pWE = * hWE ;
  138.  
  139.     // refuse all drags if the weFReadOnly feature is enabled
  140.     if ( BTST ( pWE -> features, weFReadOnly ) )
  141.     {
  142.         goto cleanup ;
  143.     }
  144.  
  145.     // count items in this drag
  146.     if ( ( err = CountDragItems ( drag, & numDragItems ) ) != noErr )
  147.     {
  148.         goto cleanup ;
  149.     }
  150.  
  151.     for ( dragItemIndex = 1 ; dragItemIndex <= numDragItems ; dragItemIndex ++ )
  152.     {
  153.         // get item reference number for current drag item
  154.         if ( ( err = GetDragItemReferenceNumber ( drag, dragItemIndex, & dragItem ) ) != noErr )
  155.         {
  156.             goto cleanup ;
  157.         }
  158.  
  159.         // see if this drag item contains a text flavor
  160.         err = _WEGetFlavor ( drag, dragItem, kTypeText, nil, kInvalidOffset, hWE ) ;
  161.  
  162. #if WASTE_OBJECTS
  163.         if ( err == badDragFlavorErr )
  164.         {
  165.             SInt32 objectIndex ;
  166.             FlavorType objectType ;
  167.  
  168.             // see if this drag item contains a flavor matching one of the registered object types
  169.             for ( objectIndex = 0 ;
  170.                   _WEGetIndObjectType ( objectIndex, & objectType, hWE ) == noErr ;
  171.                   objectIndex ++ )
  172.             {
  173.                 if ( ( err = _WEGetFlavor(drag, dragItem, objectType, nil, kInvalidOffset, hWE ) ) != badDragFlavorErr )
  174.                 {
  175.                     break ;
  176.                 }
  177.             }
  178.         }
  179. #endif
  180.  
  181.         if ( err != noErr )
  182.         {
  183.             goto cleanup ;
  184.         }
  185.     } // for
  186.  
  187.     retval = true;
  188.  
  189. cleanup:
  190.  
  191.     // unlock the WE record
  192.     _WESetHandleLock ( ( Handle ) hWE, saveWELock ) ;
  193.  
  194.     return retval ;
  195. }
  196.  
  197. pascal void _WEUpdateDragCaret ( SInt32 offset, WEHandle hWE )
  198. {
  199.     WEPtr pWE = * hWE ;        // assume WE record is already locked
  200.     UInt32 currentTime ;
  201.  
  202.     // get current time
  203.     currentTime = TickCount ( ) ;
  204.  
  205.     if ( offset == pWE -> dragCaretOffset )
  206.     {
  207.  
  208.         // drag caret offset didn't change; blink the caret
  209.         if ( ( currentTime > pWE -> caretTime + GetCaretTime ( ) ) && ( offset != kInvalidOffset ) )
  210.         {
  211.             _WEDrawCaret ( pWE -> dragCaretOffset, hilite, false, hWE ) ;
  212.             BCHG ( pWE -> flags, weFDragCaretVisible ) ;        // invert flag
  213.             pWE -> caretTime = currentTime ;
  214.         }
  215.     }
  216.     else
  217.     {
  218.  
  219.         // drag caret offset did change
  220.         // hide old caret, if it's showing
  221.         if ( BTST ( pWE -> flags, weFDragCaretVisible ) )
  222.         {
  223.             _WEDrawCaret ( pWE -> dragCaretOffset, hilite, false, hWE ) ;
  224.         }
  225.  
  226.         // show new caret (unless offset is kInvalidOffset)
  227.         if ( offset != kInvalidOffset )
  228.         {
  229.             _WEDrawCaret ( offset, hilite, false, hWE ) ;
  230.             BSET ( pWE -> flags, weFDragCaretVisible ) ;
  231.             pWE -> caretTime = currentTime ;
  232.         }
  233.         else
  234.         {
  235.             BCLR ( pWE -> flags, weFDragCaretVisible ) ;
  236.         }
  237.  
  238.         // remember drag caret offset
  239.         pWE -> dragCaretOffset = offset ;
  240.     }
  241. }
  242.  
  243. pascal OSErr WETrackDrag ( DragTrackingMessage message, DragReference drag, WEHandle hWE )
  244. {
  245.     WEPtr pWE ;
  246.     DragAttributes attributes ;
  247.     Point mouse ;
  248.     LongPt thePoint ;
  249.     SInt32 offset ;
  250.     WEEdge edge ;
  251.     Boolean saveWELock ;
  252.     OSErr err ;
  253.  
  254.     // lock the WE record
  255.     saveWELock = _WESetHandleLock ( ( Handle ) hWE, true ) ;
  256.     pWE = * hWE ;
  257.  
  258.     // dispatch on message
  259.     switch ( message )
  260.     {
  261.         case dragTrackingEnterWindow :
  262.         {
  263.             // determine whether we can accept this drag
  264.             if ( WECanAcceptDrag ( drag, hWE ) )
  265.             {
  266.                 BSET ( pWE -> flags, weFCanAcceptDrag ) ;
  267.             }
  268.             else
  269.             {
  270.                 BCLR ( pWE -> flags, weFCanAcceptDrag ) ;
  271.             }
  272.  
  273.             // reset clickTime
  274.             pWE -> clickTime = 0 ;
  275.             break ;
  276.         }
  277.  
  278.         case dragTrackingInWindow :
  279.         {
  280.             if ( BTST ( pWE -> flags, weFCanAcceptDrag ) )
  281.             {
  282.  
  283.                 // get drag attributes
  284.                 if ( ( err = GetDragAttributes ( drag, & attributes ) ) != noErr )
  285.                 {
  286.                     goto cleanup;
  287.                 }
  288.  
  289.                 // get current mouse location in local coordinates
  290.                 if ( ( err = GetDragMouse ( drag, & mouse, nil ) ) != noErr )
  291.                 {
  292.                     goto cleanup;
  293.                 }
  294.                 GlobalToLocal ( & mouse ) ;
  295.  
  296.                 if ( PtInRgn ( mouse, pWE -> viewRgn ) )
  297.                 {
  298.                     // mouse is in text area
  299.                     // hilite the text rectangle, if we haven't already
  300.                     // and if the drag has left sender window since drag tracking started
  301.                     if ( ( ! BTST ( pWE -> flags, weFHilited ) ) &&
  302.                          ( attributes & dragHasLeftSenderWindow ) )
  303.                     {
  304.                         CallWEHiliteDropAreaProc ( drag, true, hWE, pWE -> hiliteDropAreaHook ) ;
  305.                         BSET ( pWE -> flags, weFHilited ) ;
  306.                     }
  307.  
  308.                     // hide the regular caret
  309.                     if ( BTST ( pWE -> flags, weFCaretVisible ) )
  310.                     {
  311.                         _WEBlinkCaret ( hWE ) ;
  312.                     }
  313.  
  314.                     // get text offset corresponding to mouse location
  315.                     WEPointToLongPoint ( mouse, & thePoint ) ;
  316.                     offset = WEGetOffset ( & thePoint, & edge, hWE ) ;
  317.  
  318.                     // if offset is within the original selection range, don't display drag feedback
  319.                     if ( drag == pWE -> currentDrag )
  320.                     {
  321.                         if ( _WEOffsetInRange ( offset, edge, pWE -> selStart, pWE -> selEnd ) )
  322.                         {
  323.                             offset = kInvalidOffset ;
  324.                         }
  325.                     }
  326.  
  327.                     // provide a drag feedback in the form of a blinking caret
  328.                     _WEUpdateDragCaret ( offset, hWE ) ;
  329.  
  330.                     // clear clickTime
  331.                     pWE -> clickTime = 0 ;
  332.                 }
  333.                 else
  334.                 {
  335.  
  336.                     // mouse is outside text area
  337.                     // dehilite the text rectangle, if it's hilited
  338.                     if ( BTST ( pWE -> flags, weFHilited ) )
  339.                     {
  340.                         CallWEHiliteDropAreaProc ( drag, false, hWE, pWE->hiliteDropAreaHook ) ;
  341.                         BCLR ( pWE -> flags, weFHilited ) ;
  342.                     }
  343.  
  344.                     // hide the drag caret, if it's showing
  345.                     _WEUpdateDragCaret ( kInvalidOffset, hWE ) ;
  346.  
  347.                     // if the mouse has been remaining outside the view region for 10 ticks or more
  348.                     // and this drag was created by this WE instance, call the click loop routine
  349.                     if ( drag == pWE->currentDrag )
  350.                     {
  351.                         UInt32 currentTime = TickCount ( ) ;
  352.                         if ( pWE -> clickTime == 0 )
  353.                         {
  354.                             pWE -> clickTime = currentTime ;
  355.                         }
  356.                         else if ( currentTime > pWE -> clickTime + kAutoScrollDelay )
  357.                         {
  358.                             if ( pWE -> clickLoop != nil )
  359.                             {
  360.                                 CallWEClickLoopProc ( hWE, pWE -> clickLoop ) ;
  361.                             }
  362.                         }
  363.                     }
  364.                 }
  365.             }
  366.             break ;
  367.         }
  368.  
  369.         case dragTrackingLeaveWindow :
  370.         {
  371.  
  372.             // drag has left this window
  373.             // dehilite the text area if necessary
  374.             if ( BTST ( pWE -> flags, weFHilited ) )
  375.             {
  376.                 CallWEHiliteDropAreaProc ( drag, false, hWE, pWE->hiliteDropAreaHook ) ;
  377.                 BCLR ( pWE -> flags, weFHilited ) ;
  378.             }
  379.  
  380.             // hide the drag caret, if it's showing
  381.             _WEUpdateDragCaret ( kInvalidOffset, hWE ) ;
  382.             break;
  383.         }
  384.  
  385.         default:
  386.         {
  387.             break;
  388.         }
  389.     }
  390.  
  391.     // clear result code
  392.     err = noErr ;
  393.  
  394. cleanup:
  395.     // unlock the WE record
  396.     _WESetHandleLock ( ( Handle ) hWE, saveWELock ) ;
  397.  
  398.     // return result code
  399.     return err ;
  400. }
  401.  
  402. pascal OSErr WEReceiveDrag(DragReference drag, WEHandle hWE)
  403. {
  404.     WEPtr pWE;
  405.     WEActionHandle hAction;
  406.     Handle hText = nil;
  407.     Handle hStyles = nil;
  408. #if WASTE_OBJECTS
  409.     Handle hSoup = nil;
  410. #endif
  411.     Handle hObjectData = nil;
  412.     Point mouse;
  413.     LongPt dropLocation;
  414.     SInt32 insertionOffset;
  415.     SInt32 insertionLength;
  416.     SInt32 sourceStart;
  417.     SInt32 sourceEnd;
  418.     SInt32 destStart;
  419.     SInt32 destEnd;
  420.     SInt32 delta;
  421.     UInt16 dragItemIndex;
  422.     UInt16 numDragItems;
  423.     ItemReference dragItem;
  424.     GrafPtr savePort;
  425.     SInt16 intPasteAction;
  426.     WEEdge dropEdge;
  427.     char space = kSpace;
  428.     Boolean isMove = false;
  429.     Boolean isBackwards;
  430.     Boolean saveWELock;
  431.     OSErr err;
  432.  
  433.     // lock the WE record
  434.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  435.     pWE = *hWE;
  436.  
  437.     // set up the port
  438.     GetPort(&savePort);
  439.     SetPort(pWE->port);
  440.  
  441.     // stop any ongoing inline input session
  442.     WEStopInlineSession(hWE);
  443.  
  444.     // hide the drag caret
  445.     _WEUpdateDragCaret(kInvalidOffset, hWE);
  446.  
  447.     // refuse this drag if it doesn't taste good
  448.     err = badDragFlavorErr;
  449.     if (!WECanAcceptDrag(drag, hWE))
  450.     {
  451.         goto cleanup;
  452.     }
  453.  
  454.     // get drop location in local coordinates
  455.     if ((err = GetDragMouse(drag, &mouse, nil)) != noErr)
  456.     {
  457.         goto cleanup;
  458.     }
  459.     GlobalToLocal(&mouse);
  460.  
  461.     // for the drag to be accepted, the drop location must be within the view region
  462.     err = dragNotAcceptedErr;
  463.     if (!PtInRgn(mouse, pWE->viewRgn))
  464.     {
  465.         goto cleanup;
  466.     }
  467.  
  468.     // get drop offset into the text
  469.     WEPointToLongPoint(mouse, &dropLocation);
  470.     insertionOffset = WEGetOffset(&dropLocation, &dropEdge, hWE);
  471.     // destStart/destEnd define the range to highlight at the end of the drag
  472.     destStart = insertionOffset;
  473.  
  474.     // drag originated from this same window?
  475.     if (drag == pWE->currentDrag)
  476.     {
  477.  
  478.         // sourceStart/sourceEnd define the range to delete at the end of the move
  479.         sourceStart = pWE->selStart;
  480.         sourceEnd = pWE->selEnd;
  481.  
  482.         // remember text length before insertion
  483.         delta = pWE->textLength;
  484.  
  485.         // if insertion offset is within the original selection range, abort the drag
  486.         // (*err = dragNotAcceptedErr;*)
  487.         if (_WEOffsetInRange(insertionOffset, dropEdge, sourceStart, sourceEnd))
  488.         {
  489.             goto cleanup;
  490.         }
  491.  
  492.         // if the drag originated from this window, a move,
  493.         // rather than a copy, should be performed
  494.         // Exception: the option key may be held down at mouse-down
  495.         // or mouse-up time to force a copy operation.
  496.  
  497.         isMove = !_WEIsOptionDrag(drag);
  498.         isBackwards = (insertionOffset <= sourceStart);
  499.     } // if intra-window drag
  500.  
  501.     // clear null style
  502.     BCLR(pWE->flags, weFUseNullStyle);
  503.  
  504.     // hide selection highlighting
  505.     _WEHiliteRange(pWE->selStart, pWE->selEnd, hWE);
  506.  
  507.     // increment modification count
  508.     pWE->modCount++;
  509.  
  510.     // if undo support is enabled, create a new action so we'll be able to undo the insertion
  511.     if (BTST(pWE->features, weFUndoSupport))
  512.     {
  513.         WEClearUndo(hWE);
  514.         if (_WENewAction(insertionOffset, insertionOffset, 0, weAKDrag, 0, hWE, &hAction) == noErr)
  515.         {
  516.             _WEPushAction(hAction);
  517.         }
  518.     }
  519.  
  520.     // count items in this drag
  521.     if ((err = CountDragItems(drag, &numDragItems)) != noErr)
  522.     {
  523.         goto cleanup;
  524.     }
  525.  
  526.     for (dragItemIndex = 1; dragItemIndex<=numDragItems; dragItemIndex++)
  527.     {
  528.         // get item reference number for current drag item
  529.         if ((err = GetDragItemReferenceNumber(drag, dragItemIndex, &dragItem)) != noErr)
  530.         {
  531.             goto cleanup;
  532.         }
  533.  
  534.         // see if this drag item contains a text flavor
  535.         err = _WEExtractFlavor(drag, dragItem, kTypeText, &hText, insertionOffset, hWE);
  536.         if (err == noErr)
  537.         {
  538.             if (!BTST(pWE->features, weFMonoStyled))
  539.             {
  540.                 // extract accompanying styles and soup, if any
  541.                 err = _WEExtractFlavor(drag, dragItem, kTypeStyles, &hStyles, insertionOffset, hWE);
  542.                 if ((err != noErr) && (err != badDragFlavorErr))
  543.                 {
  544.                     goto cleanup;
  545.                 }
  546. #if WASTE_OBJECTS
  547.                 err = _WEExtractFlavor(drag, dragItem, kTypeSoup, &hSoup, insertionOffset, hWE);
  548.                 if ((err != noErr) && (err != badDragFlavorErr))
  549.                 {
  550.                     goto cleanup;
  551.                 }
  552. #endif
  553.             }
  554.  
  555.             // any extra space added because of intelligent cut-and-paste rules will use the
  556.             // style attributes set at the insertion point
  557.             if (dragItemIndex == 1)
  558.             {
  559.                 pWE->selStart = insertionOffset;
  560.                 pWE->selEnd = insertionOffset;
  561.                 _WESynchNullStyle(hWE);
  562.             }
  563.  
  564.             // get text length
  565.             insertionLength = GetHandleSize(hText);
  566.             destEnd = insertionOffset + insertionLength;
  567.  
  568.             // insert the new text at the insertion point
  569.             HLock(hText);
  570.             err = _WEInsertText(insertionOffset, *hText, insertionLength, hWE);
  571.             _WEForgetHandle(&hText);
  572.             if (err != noErr)
  573.             {
  574.                 goto cleanup;
  575.             }
  576.  
  577.             // adjust deletion range length in undo buffer
  578.             _WEAdjustUndoRange(insertionLength, hWE);
  579.  
  580.             // apply the accompanying styles, if any
  581.             if (hStyles != nil)
  582.             {
  583.                 if ((err = _WEApplyStyleScrap(insertionOffset, destEnd,
  584.                             (StScrpHandle) hStyles, hWE)) != noErr)
  585.                 {
  586.                     goto cleanup;
  587.                 }
  588.                 _WEForgetHandle(&hStyles);
  589.             }
  590.  
  591. #if WASTE_OBJECTS
  592.             // apply the accompanying soup, if any
  593.             if (hSoup != nil)
  594.             {
  595.                 if ((err = _WEApplySoup(insertionOffset, hSoup, hWE)) != noErr)
  596.                 {
  597.                     goto cleanup;
  598.                 }
  599.                 _WEForgetHandle(&hSoup);
  600.             }
  601. #endif
  602.  
  603.             // determine whether an extra space should be added before or after the inserted text
  604.             intPasteAction = _WEIntelligentPaste(insertionOffset, destEnd, hWE);
  605.  
  606.             // add the extra space, if necessary
  607.             if (intPasteAction != weDontAddSpaces)
  608.             {
  609.                 if (intPasteAction == weAddSpaceOnLeftSide)
  610.                 {
  611.                     if ((err = _WEInsertText(insertionOffset, &space, sizeof(space), hWE)) != noErr)
  612.                     {
  613.                         goto cleanup;
  614.                     }
  615.  
  616.                     destEnd++;
  617.  
  618.                     // if an extra space is inserted in front of all dropped items,
  619.                     // don't count it when eventually highlighting the destination range
  620.                     if (dragItemIndex == 1)
  621.                     {
  622.                         destStart++;
  623.                     }
  624.                 }
  625.                 else
  626.                 {
  627.                     if ((err = _WEInsertText(destEnd, &space, sizeof(space), hWE)) != noErr)
  628.                     {
  629.                         goto cleanup;
  630.                     }
  631.                 }
  632.  
  633.                 insertionLength++;
  634.                 _WEAdjustUndoRange(1, hWE);
  635.             } // if extra space
  636.  
  637.         }
  638.  
  639. #if WASTE_OBJECTS
  640.         else if (err == badDragFlavorErr)
  641.         {
  642.             SInt32 objectIndex;
  643.             FlavorType objectType;
  644.             Point objectSize;
  645.             SInt16 saveUndoSupport;
  646.             SInt16 saveInhibitRecal;
  647.  
  648.             objectSize.v = 0;
  649.             objectSize.h = 0;
  650.  
  651.             // no text flavor: there must be a flavor matching one of the registered object types
  652.             objectIndex = 0;
  653.             while (_WEGetIndObjectType(objectIndex, &objectType, hWE) == noErr)
  654.             {
  655.                 err = _WEExtractFlavor(drag, dragItem, objectType, &hObjectData, insertionOffset, hWE);
  656.                 if (err == noErr)
  657.                 {
  658.                     break;     // enclosing while
  659.                 }
  660.                 if (err != badDragFlavorErr)
  661.                 {
  662.                     goto cleanup;
  663.                 }
  664.                 objectIndex++;
  665.             } // while
  666.             if (err != noErr)
  667.             {
  668.                 goto cleanup;
  669.             }
  670.  
  671.             // set insertion point on first iteration (*after* extracting flavors, in case we are
  672.             // doing an intra-window move, otherwise our send proc would be confused)
  673.             if (dragItemIndex == 1)
  674.             {
  675.                 pWE->selStart = insertionOffset;
  676.                 pWE->selEnd = insertionOffset;
  677.             }
  678.  
  679.             // insert the object, but without touching undo or redrawing the text
  680.             saveUndoSupport = WEFeatureFlag(weFUndoSupport, weBitClear, hWE);
  681.             saveInhibitRecal = WEFeatureFlag(weFInhibitRecal, weBitSet, hWE);
  682.             err = WEInsertObject(objectType, hObjectData, objectSize, hWE);
  683.             WEFeatureFlag(weFUndoSupport, saveUndoSupport, hWE);
  684.             WEFeatureFlag(weFInhibitRecal, saveInhibitRecal, hWE);
  685.             if (err != noErr)
  686.             {
  687.                 goto cleanup;
  688.             }
  689.  
  690.             insertionLength = 1;
  691.             destEnd = insertionOffset + 1;
  692.             pWE->modCount--; // compensate for increment made by WEInsertObject
  693.             _WEAdjustUndoRange(1, hWE);
  694.         }
  695. #endif    // WASTE_OBJECTS
  696.  
  697.         else
  698.             goto cleanup;
  699.  
  700.         // advance insertion offset for subsequent drag items, if any
  701.         insertionOffset += insertionLength;
  702.  
  703.     } // for
  704.  
  705.     if (isMove)
  706.     {
  707.         // adjust source range
  708.         if (isBackwards)
  709.         {
  710.             delta -= pWE->textLength;
  711.             sourceStart -= delta;
  712.             sourceEnd -= delta;
  713.         }
  714.  
  715.         // extend range according to intelligent cut-and-paste rules
  716.         _WEIntelligentCut(&sourceStart, &sourceEnd, hWE);
  717.  
  718.         // if undo support is enabled, create a new action so we'll be able to undo the deletion
  719.         if (BTST(pWE->features, weFUndoSupport))
  720.         {
  721.             if (_WENewAction(sourceStart, sourceEnd, 0, weAKDrag, 0, hWE, &hAction) == noErr)
  722.             {
  723.                 _WEPushAction(hAction);
  724.             }
  725.         }
  726.  
  727.         // delete source range
  728.         delta = pWE->textLength;
  729.         if ((err = _WEDeleteRange(sourceStart, sourceEnd, hWE)) != noErr)
  730.         {
  731.             goto cleanup;
  732.         }
  733.  
  734.         // adjust destination range
  735.         if (!isBackwards)
  736.         {
  737.             delta -= pWE->textLength;
  738.             destStart -= delta;
  739.             destEnd -= delta;
  740.         }
  741.  
  742.     } // if isMove
  743.  
  744.     // select the range encompassing all items dropped
  745.     pWE->selStart = destStart;
  746.     pWE->selEnd = destEnd;
  747.  
  748.     // redraw the text
  749.     if (isMove)
  750.     {
  751.         if (sourceStart < destStart)
  752.         {
  753.             err = _WERedraw(sourceStart, destEnd, hWE);
  754.         }
  755.         else
  756.         {
  757.             err = _WERedraw(destStart, sourceEnd, hWE);
  758.         }
  759.     }
  760.     else
  761.     {
  762.         err = _WERedraw(destStart, destEnd, hWE);
  763.     }
  764.  
  765. cleanup:
  766.     // dispose of temporary handles
  767.     _WEForgetHandle(&hText);
  768.     _WEForgetHandle(&hStyles);
  769. #if WASTE_OBJECTS
  770.     _WEForgetHandle(&hSoup);
  771. #endif
  772.  
  773.     // restore the port
  774.     SetPort(savePort);
  775.  
  776.     // unlock the WE record
  777.     _WESetHandleLock((Handle) hWE, saveWELock);
  778.  
  779.     // return result code
  780.     return err;
  781.  
  782. }
  783.  
  784. static pascal OSErr _WESendFlavor(FlavorType requestedType, void *dragSendRefCon, WEHandle hWE, DragReference drag)
  785. {
  786. #pragma unused(dragSendRefCon)
  787.  
  788. #if !GENERATINGCFM
  789.     SInt32 saveA5 = SetCurrentA5();    // this fixes a conflict with HoverBar
  790.                                     // (well, probably a bug in the Drag Manager)
  791. #endif
  792.  
  793.     WEPtr pWE = *hWE;
  794.     SInt32 selStart = pWE->selStart;
  795.     SInt32 selEnd = pWE->selEnd;
  796.     Handle hItem = nil;
  797.     Boolean disposeItem = true;        // dispose of item when done
  798. #if WASTE_OBJECTS
  799.     WEObjectDescHandle hObjectDesc;
  800. #endif
  801.     OSErr err;
  802.  
  803.     // allocate a temporary handle to hold a copy of the requested flavor
  804.     if ((err = _WEAllocate(0, kAllocTemp, &hItem)) != noErr)
  805.     {
  806.         goto cleanup;
  807.     }
  808.  
  809. #if WASTE_OBJECTS
  810.     // see if the selection contains an embedded object whose type matches the flavortype
  811.     if (WEGetSelectedObject(&hObjectDesc, hWE) == noErr)
  812.     {
  813.         FlavorType theType;
  814.  
  815.         if ((err = _WEStreamObject(weToDrag, &theType, &hItem, &disposeItem, hObjectDesc)) != noErr)
  816.         {
  817.             goto cleanup;
  818.         }
  819.  
  820.         // make sure theType matches the requested type
  821.         err = badDragFlavorErr;
  822.         if (theType != requestedType)
  823.         {
  824.             goto cleanup;
  825.         }
  826.     }
  827.     else
  828. #endif
  829.     {
  830.         // identify the requested flavor type as either 'TEXT', 'styl' or 'SOUP'
  831.         if (requestedType == kTypeText)
  832.         {
  833.             err = WECopyRange(selStart, selEnd, hItem, nil, nil, hWE);
  834.         }
  835.         else if (requestedType == kTypeStyles)
  836.         {
  837.             err = WECopyRange(selStart, selEnd, nil, hItem, nil, hWE);
  838.         }
  839. #if WASTE_OBJECTS
  840.         else if (requestedType == kTypeSoup)
  841.         {
  842.             err = WECopyRange(selStart, selEnd, nil, nil, hItem,hWE);
  843.         }
  844. #endif
  845.         else
  846.         {
  847.             err = badDragFlavorErr;
  848.         }
  849.  
  850.         if (err != noErr)
  851.         {
  852.             goto cleanup;
  853.         }
  854.     }
  855.  
  856.     // set the drag flavor data
  857.     HLock(hItem);
  858.     err = SetDragItemFlavorData(drag, (ItemReference) hWE, requestedType, *hItem,
  859.             GetHandleSize(hItem), 0);
  860.     HUnlock(hItem);
  861.  
  862. cleanup:
  863.     if (disposeItem)
  864.     {
  865.         _WEForgetHandle(&hItem);
  866.     }
  867.  
  868. #if !GENERATINGCFM
  869.     SetA5(saveA5);
  870. #endif
  871.  
  872.     // return result code
  873.     return err;
  874.  
  875. }
  876.  
  877. pascal Boolean WEDraggedToTrash(DragReference drag)
  878. {
  879.  
  880.     // return true if the drop location of the specified drag is the trash
  881.  
  882.     const int bDirectoryAttr = 4;
  883.  
  884.     AEDesc dropLocation, coercedDropLocation;
  885.     CInfoPBRec pb;
  886.     FSSpecPtr pSpec;
  887.     SInt16 trashVRefNum;
  888.     SInt32 trashDirID;
  889.     Boolean draggedToTrash;
  890.  
  891.     draggedToTrash = false;
  892.     dropLocation.dataHandle = nil;
  893.     coercedDropLocation.dataHandle = nil;
  894.  
  895.     // get drop location
  896.     if (GetDropLocation(drag, &dropLocation) != noErr)
  897.     {
  898.         goto cleanup;
  899.     }
  900.  
  901.     // do nothing if dropLocation is a null descriptor
  902.     if (dropLocation.descriptorType == typeNull)
  903.     {
  904.         goto cleanup;
  905.     }
  906.  
  907.     // try to coerce the descriptor to a file system specification record
  908.     if (AECoerceDesc(&dropLocation, typeFSS, &coercedDropLocation) != noErr)
  909.     {
  910.         goto cleanup;
  911.     }
  912.  
  913.     // lock the data handle of the coerced descriptor
  914.     HLock(coercedDropLocation.dataHandle);
  915.     pSpec = *(FSSpecHandle)coercedDropLocation.dataHandle;
  916.  
  917.     // determine the directory ID of the drop location (assuming it's a folder!)
  918.     BLOCK_CLR(pb);
  919.     pb.hFileInfo.ioVRefNum = pSpec->vRefNum;
  920.     pb.hFileInfo.ioDirID = pSpec->parID;
  921.     pb.hFileInfo.ioNamePtr = pSpec->name;
  922.     if (PBGetCatInfoSync(&pb) != noErr)
  923.     {
  924.         goto cleanup;
  925.     }
  926.  
  927.     // make sure the specified file system object is really a directory
  928.     if (!BTST(pb.hFileInfo.ioFlAttrib, bDirectoryAttr))
  929.     {
  930.         goto cleanup;
  931.     }
  932.  
  933.     // find the directory ID of the trash folder
  934.     if (FindFolder(pSpec->vRefNum, kTrashFolderType, kDontCreateFolder, &trashVRefNum, &trashDirID) != noErr)
  935.     {
  936.         goto cleanup;
  937.     }
  938.  
  939.     // compare the two directory IDs: if they're the same, the drop location is the trash
  940.     if (pb.dirInfo.ioDrDirID == trashDirID)
  941.     {
  942.         draggedToTrash = true;
  943.     }
  944.  
  945. cleanup:
  946.     // clean up
  947.     AEDisposeDesc(&dropLocation);
  948.     AEDisposeDesc(&coercedDropLocation);
  949.  
  950.     return draggedToTrash;
  951. }
  952.  
  953. pascal RgnHandle _WEOutlineRgn ( RgnHandle solidRgn )
  954. {
  955.     RgnHandle outlineRgn ;
  956.  
  957.     //    make an outline region out of the given solid region
  958.     if ( ( outlineRgn = NewRgn ( ) ) != nil )
  959.     {
  960.         CopyRgn ( solidRgn, outlineRgn ) ;
  961.         InsetRgn ( outlineRgn, 1, 1 ) ;
  962.         DiffRgn ( solidRgn, outlineRgn, outlineRgn ) ;
  963.     }
  964.  
  965.     return outlineRgn ;
  966. }
  967.  
  968. #if WASTE_TRANSLUCENT_DRAGS
  969.  
  970. pascal OSErr _WEMakeDragImage ( GWorldPtr * imageGWorld, RgnHandle * imageRgn, WEHandle hWE )
  971. {
  972.     WEPtr pWE = * hWE ;        //    assume WE record is already locked
  973.     PixMapHandle pixels = nil ;
  974.     Point offset ;
  975.     Rect localBounds ;
  976.     Rect globalBounds ;
  977.     SInt32 dragArea ;
  978.     GDHandle saveDevice ;
  979.     CGrafPtr savePort ;
  980.     GrafPtr textPort ;
  981.     Boolean wasInactive = false ;
  982.     OSErr err ;
  983.  
  984.     //    init return values
  985.     * imageGWorld = nil ;
  986.     * imageRgn = nil ;
  987.  
  988.     //    save current graphics world
  989.     GetGWorld ( & savePort, & saveDevice ) ;
  990.  
  991.     //    set up the port
  992.     textPort = pWE -> port ;
  993.     SetPort ( textPort ) ;
  994.  
  995.     //    calculate delta between global coords and local coords
  996.     offset . v = 0 ;
  997.     offset . h = 0 ;
  998.     LocalToGlobal ( & offset ) ;
  999.  
  1000.     //    get image region (in local coordinates)
  1001.     if ( ( * imageRgn = WEGetHiliteRgn ( pWE -> selStart, pWE -> selEnd, hWE ) ) == nil )
  1002.     {
  1003.         err = memFullErr ;
  1004.         goto cleanup ;
  1005.     }
  1006.  
  1007.     //    get bounding rectangle of image region
  1008.     localBounds = ( ** imageRgn ) -> rgnBBox ;
  1009.  
  1010.     //    translate to global coordinates
  1011.     globalBounds = localBounds ;
  1012.     OffsetRect ( & globalBounds, offset . h, offset . v ) ;
  1013.  
  1014.     //    calculate drag region area
  1015.     dragArea = ( ( SInt32 ) globalBounds . right - globalBounds . left ) *
  1016.                ( ( SInt32 ) globalBounds . bottom - globalBounds . top ) ;
  1017.  
  1018.     //    if translucent drags are available and drag region area
  1019.     //    is reasonably small, prepare a drag image
  1020.     if ( ( dragArea <= pWE -> translucencyThreshold ) && BTST ( pWE -> flags, weFHasTranslucentDrags ) )
  1021.     {
  1022.  
  1023.         //    create an 8-bit deep gworld for drawing the drag image
  1024.         if ( ( err = NewGWorld ( imageGWorld, 8, & globalBounds, nil, nil, useTempMem ) ) == noErr )
  1025.         {
  1026.             //    get the pixmap from the gworld
  1027.             pixels = GetGWorldPixMap ( * imageGWorld ) ;
  1028.  
  1029.             //    set up the gworld
  1030.             SetGWorld ( * imageGWorld, nil ) ;
  1031.             LockPixels ( pixels ) ;
  1032.             SetOrigin ( localBounds . left, localBounds . top ) ;
  1033.  
  1034.             //    temporarily set weFActive so we'll draw the full highlighting
  1035.             if ( ! BTST ( pWE -> flags, weFActive ) )
  1036.             {
  1037.                 wasInactive = true ;
  1038.                 BSET ( pWE -> flags, weFActive ) ;
  1039.             }
  1040.  
  1041.             //    draw the selection into the gworld
  1042.             EraseRect ( & localBounds ) ;
  1043.             pWE -> port = ( GrafPtr ) * imageGWorld ;
  1044.             WEUpdate ( * imageRgn, hWE ) ;
  1045.             pWE -> port = textPort ;
  1046.  
  1047.             //    restore original setting of weFActive
  1048.             if ( wasInactive )
  1049.             {
  1050.                 BCLR ( pWE -> flags, weFActive ) ;
  1051.             }
  1052.  
  1053.             //    end drawing
  1054.             UnlockPixels ( pixels ) ;
  1055.             SetOrigin ( 0, 0 ) ;
  1056.         }
  1057.     }
  1058.  
  1059.     //    convert the image region to global coordinates
  1060.     OffsetRgn ( * imageRgn, offset . h, offset . v ) ;
  1061.  
  1062.     if ( pixels != nil )
  1063.     {
  1064.         //    convert the image itself to global coordinates
  1065.         offset = topLeft ( ( ** imageRgn ) -> rgnBBox ) ;
  1066.         OffsetRect ( & ( * pixels ) -> bounds, offset . h, offset . v ) ;
  1067.     }
  1068.  
  1069.     //    clear result code
  1070.     err = noErr ;
  1071.  
  1072. cleanup :
  1073.     //    restore original graphics world
  1074.     SetGWorld ( savePort, saveDevice ) ;
  1075.  
  1076.     //    return result code
  1077.     return err ;
  1078. }
  1079.  
  1080. #endif    // WASTE_TRANSLUCENT_DRAGS
  1081.  
  1082. pascal OSErr _WEDrag ( Point mouseLoc, EventModifiers modifiers, UInt32 clickTime, WEHandle hWE )
  1083. {
  1084.     WEPtr pWE = * hWE ;        //    assume WE record is already locked
  1085.     DragReference drag = kNullDrag ;
  1086.     RgnHandle dragRgn = nil ;
  1087.     RgnHandle dragOutline = nil ;
  1088.     EventRecord event ;
  1089.     Rect dragBounds ;
  1090.     Point tempPoint ;
  1091.     GrafPtr savePort ;
  1092.     OSErr err ;
  1093. #if WASTE_OBJECTS
  1094.     WEObjectDescHandle hObjectDesc ;
  1095. #endif
  1096. #if WASTE_TRANSLUCENT_DRAGS
  1097.     GWorldPtr dragGWorld = nil ;
  1098. #endif
  1099.  
  1100.     // set up the port
  1101.     GetPort ( & savePort ) ;
  1102.     SetPort ( pWE -> port ) ;
  1103.  
  1104.     // turn the cursor into an arrow
  1105.     SetCursor ( & qd . arrow ) ;
  1106.  
  1107.     // fabricate an EventRecord for TrackDrag
  1108.     event . what = mouseDown ;
  1109.     event . message = 0 ;
  1110.     event . when = clickTime ;
  1111.     event . where = mouseLoc ;
  1112.     LocalToGlobal ( & event . where ) ;
  1113.     event . modifiers = modifiers ;
  1114.  
  1115.     // to start the drag, the user must move the mouse a certain distance
  1116.     // away from the initial mouse location; this allows a short click in
  1117.     // the selection area to set the insertion point
  1118.     err = weNoDragErr ;
  1119.     if ( ! WaitMouseMoved ( event . where ) )
  1120.     {
  1121.         goto cleanup ;
  1122.     }
  1123.  
  1124.     // create a drag object
  1125.     if ( ( err = NewDrag ( & drag ) ) != noErr )
  1126.     {
  1127.         goto cleanup ;
  1128.     }
  1129.  
  1130. #if WASTE_OBJECTS
  1131.     // if the selection range consists of an embedded object,
  1132.     // then use its object type as flavor type
  1133.     if ( WEGetSelectedObject ( & hObjectDesc, hWE ) == noErr )
  1134.     {
  1135.         FlavorType theType ;
  1136.         Handle theData = nil ;
  1137.         Boolean disposeData ;
  1138.  
  1139.         if ( ( err = _WEStreamObject ( weToDrag, & theType, & theData, & disposeData, hObjectDesc ) ) != noErr )
  1140.         {
  1141.             goto cleanup ;
  1142.         }
  1143.  
  1144.         if ( ( err = AddDragItemFlavor ( drag, ( ItemReference ) hWE, theType, nil, 0, 0 ) ) != noErr )
  1145.         {
  1146.             goto cleanup ;
  1147.         }
  1148.     }
  1149.     else
  1150. #endif
  1151.     {
  1152.  
  1153.         // add a 'TEXT' flavor to the drag
  1154.         if ( ( err = AddDragItemFlavor ( drag, ( ItemReference ) hWE, kTypeText, nil, 0, 0 ) ) != noErr )
  1155.         {
  1156.             goto cleanup ;
  1157.         }
  1158.  
  1159.         // add a 'styl' flavor to the drag
  1160.         if ( ( err = AddDragItemFlavor ( drag, ( ItemReference ) hWE, kTypeStyles, nil, 0, 0 ) ) != noErr )
  1161.         {
  1162.             goto cleanup ;
  1163.         }
  1164.  
  1165. #if WASTE_OBJECTS
  1166.         // add a 'SOUP' flavor to the drag
  1167.         if ( ( err = AddDragItemFlavor ( drag, ( ItemReference ) hWE, kTypeSoup, nil, 0, 0 ) ) != noErr )
  1168.         {
  1169.             goto cleanup ;
  1170.         }
  1171. #endif
  1172.  
  1173.     }
  1174.  
  1175.     // since we didn't provide the flavor data for any of the above flavors,
  1176.     // we need supply a data send callback
  1177.     if ( _weFlavorSender == nil )
  1178.     {
  1179.         _weFlavorSender = NewDragSendDataProc ( _WESendFlavor ) ;
  1180.     }
  1181.  
  1182.     if ( ( err = SetDragSendProc ( drag, _weFlavorSender, 0 ) ) != noErr )
  1183.     {
  1184.         goto cleanup ;
  1185.     }
  1186.  
  1187. #if WASTE_TRANSLUCENT_DRAGS
  1188.     //    get drag image & region
  1189.     if ( ( err = _WEMakeDragImage( & dragGWorld, & dragRgn, hWE ) ) != noErr )
  1190.     {
  1191.         goto cleanup ;
  1192.     }
  1193. #else
  1194.     //    get drag region (in local coordinates)
  1195.     if ( ( dragRgn = WEGetHiliteRgn ( pWE -> selStart, pWE -> selEnd, hWE ) ) == nil )
  1196.     {
  1197.         err = memFullErr ;
  1198.         goto cleanup ;
  1199.     }
  1200.  
  1201.     //    convert drag region to global coordinates
  1202.     tempPoint . v = 0 ;
  1203.     tempPoint . h = 0 ;
  1204.     LocalToGlobal ( & tempPoint ) ;
  1205.     OffsetRgn ( dragRgn, tempPoint . h, tempPoint . v ) ;
  1206. #endif
  1207.  
  1208.     //    set the bounds of the drag
  1209.     dragBounds = ( * dragRgn ) -> rgnBBox ;
  1210.     if ( ( err = SetDragItemBounds ( drag, ( ItemReference ) hWE, & dragBounds ) ) != noErr )
  1211.     {
  1212.         goto cleanup ;
  1213.     }
  1214.  
  1215. #if WASTE_TRANSLUCENT_DRAGS
  1216.     //    set drag image, if any
  1217.     if ( dragGWorld != nil )
  1218.     {
  1219.         tempPoint . v = 0 ;
  1220.         tempPoint . h = 0 ;
  1221.         SetDragImage ( drag, GetGWorldPixMap ( dragGWorld ), dragRgn, tempPoint, dragStandardImage ) ;
  1222.     }
  1223. #endif
  1224.  
  1225.     // create an outline region for TrackDrag
  1226.     dragOutline = _WEOutlineRgn ( dragRgn ) ;
  1227.  
  1228.     // stash drag reference in currentDrag so WETrackDrag and WEReceiveDrag
  1229.     // can tell whether a given drag originated from this WE instance
  1230.     pWE -> currentDrag = drag ;
  1231.  
  1232.     // track the drag
  1233.     err = TrackDrag ( drag, & event, dragOutline ) ;
  1234.     pWE -> currentDrag = kNullDrag ;
  1235.     if (err != noErr)
  1236.     {
  1237.         goto cleanup ;
  1238.     }
  1239.  
  1240.     // if the selection was dragged to the trash and the option key wasn't held down
  1241.     // and if the instance is editable, delete the selection
  1242.     if ( ! BTST ( pWE -> features, weFReadOnly ) )
  1243.     {
  1244.         if ( WEDraggedToTrash ( drag ) )
  1245.         {
  1246.             if ( ! _WEIsOptionDrag ( drag ) )
  1247.             {
  1248.                 if ( ( err = WEDelete ( hWE ) ) != noErr )
  1249.                 {
  1250.                     goto cleanup ;
  1251.                 }
  1252.             }
  1253.         }
  1254.     }
  1255.  
  1256.     // clear result code
  1257.     err = noErr ;
  1258.  
  1259. cleanup:
  1260.     // dispose of the drag
  1261.     if ( drag != kNullDrag )
  1262.     {
  1263.         DisposeDrag ( drag ) ;
  1264.     }
  1265.  
  1266. #if WASTE_TRANSLUCENT_DRAGS
  1267.     //    dispose of the image gworld
  1268.     if ( dragGWorld != nil )
  1269.     {
  1270.         DisposeGWorld ( dragGWorld ) ;
  1271.     }
  1272. #endif
  1273.  
  1274.     //    dispose of auxiliary regions
  1275.     if ( dragRgn != nil )
  1276.     {
  1277.         DisposeRgn ( dragRgn ) ;
  1278.     }
  1279.     if ( dragOutline != nil )
  1280.     {
  1281.         DisposeRgn ( dragOutline ) ;
  1282.     }
  1283.  
  1284.     // restore the port
  1285.     SetPort ( savePort ) ;
  1286.  
  1287.     // return result code
  1288.     return err;
  1289. }
  1290.  
  1291. #if WASTE_IC_SUPPORT
  1292.  
  1293. pascal void _WEResolveURL(EventModifiers modifiers, SInt32 urlStart, SInt32 urlEnd, WEHandle hWE)
  1294. {
  1295.     WEPtr pWE = *hWE;            // assume WE record is already locked
  1296.     Str255 hint;
  1297.     FourCharCode signature;
  1298.     ProcessSerialNumber psn;
  1299.     ProcessInfoRec info;
  1300.     ICInstance inst;
  1301.     ICError err;
  1302.     SInt32 junkLong;
  1303.     Handle hURL = nil;
  1304.     Boolean saveTextLock;
  1305.  
  1306.     // get the hint string IC will use to parse slack URLs
  1307.     hint[0] = 0;
  1308.     if (pWE->hURLHint != nil)
  1309.     {
  1310.         BlockMoveData(*pWE->hURLHint, hint, StrLength(*pWE->hURLHint) + 1);
  1311.     }
  1312.  
  1313.     // find out the signature of the current process
  1314.     signature = '\?\?\?\?';
  1315.     psn.highLongOfPSN = 0;
  1316.     psn.lowLongOfPSN = 0;
  1317.     if (GetCurrentProcess(&psn) == noErr)
  1318.     {
  1319.         BLOCK_CLR(info);
  1320.         info.processInfoLength = sizeof(info);
  1321.         if (GetProcessInformation(&psn, &info) == noErr)
  1322.         {
  1323.             signature = info.processSignature;
  1324.         }
  1325.     }
  1326.  
  1327.     if (ICStart(&inst, signature) == noErr)
  1328.     {
  1329.         if (ICFindConfigFile(inst, 0, nil) == noErr)
  1330.         {
  1331.             saveTextLock = _WESetHandleLock(pWE->hText, true);
  1332.  
  1333.             // cmd + option click just highlights the URL,
  1334.             // without actually resolving it
  1335.             if (modifiers & optionKey)
  1336.             {
  1337.                 hURL = NewHandle(0);
  1338.                 err = ICParseURL(inst, hint, *pWE->hText, WEGetTextLength(hWE), &urlStart, &urlEnd, hURL);
  1339.                 _WEForgetHandle(&hURL);
  1340.             }
  1341.             else
  1342.             {
  1343.                 err = ICLaunchURL(inst, hint, *pWE->hText, WEGetTextLength(hWE), &urlStart, &urlEnd);
  1344.             }
  1345.             _WESetHandleLock(pWE->hText, saveTextLock);
  1346.             WESetSelection(urlStart, urlEnd, hWE);
  1347.  
  1348.             // flash selection if successful (unless option key was down)
  1349.             if ((err == noErr) && !(modifiers & optionKey))
  1350.             {
  1351.                 Delay(5, &junkLong);
  1352.                 WEDeactivate(hWE);
  1353.                 Delay(5, &junkLong);
  1354.                 WEActivate(hWE);
  1355.             }
  1356.         }
  1357.         ICStop(inst);
  1358.     }
  1359. }
  1360.  
  1361. #endif    // WASTE_IC_SUPPORT
  1362.  
  1363. pascal void WEClick(Point mouseLoc, EventModifiers modifiers, UInt32 clickTime, WEHandle hWE)
  1364. {
  1365.     WEPtr pWE;
  1366.     LongPt thePoint;
  1367.     SInt32 offset, anchor;
  1368.     SInt32 rangeStart, rangeEnd;
  1369.     WEEdge edge;
  1370.     Boolean isMultipleClick;
  1371.     Boolean saveWELock;
  1372. #if WASTE_OBJECTS
  1373.     WEObjectDescHandle hObjectDesc;
  1374. #endif
  1375. #if WASTE_IC_SUPPORT
  1376.     SInt32 urlStart, urlEnd;
  1377. #endif
  1378.  
  1379.     // stop any ongoing inline input session
  1380.     WEStopInlineSession(hWE);
  1381.  
  1382.     // lock the WE record
  1383.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  1384.     pWE = *hWE;
  1385.  
  1386. #if WASTE_IC_SUPPORT
  1387.     // remember the selection range before the click
  1388.     urlStart = pWE->selStart;
  1389.     urlEnd = pWE->selEnd;
  1390. #endif
  1391.  
  1392.     // hide the caret if it's showing
  1393.     if (BTST(pWE->flags, weFCaretVisible))
  1394.     {
  1395.         _WEBlinkCaret(hWE);
  1396.     }
  1397.  
  1398.     // find click offset
  1399.     WEPointToLongPoint(mouseLoc, &thePoint);
  1400.     offset = WEGetOffset(&thePoint, &edge, hWE);
  1401.  
  1402.     // determine whether this click is part of a sequence
  1403.     // a single click inside an object selects it, so it's like a double click in a word
  1404.     isMultipleClick = ((clickTime < pWE->clickTime + GetDoubleClickTime()) && (offset == pWE->clickLoc));
  1405.  
  1406.     // remember click time, click offset and edge value
  1407.     pWE->clickTime = clickTime;
  1408.     pWE->clickLoc = offset;
  1409.     pWE->clickEdge = edge;
  1410.  
  1411. #if WASTE_OBJECTS
  1412.     // when selected, embedded objects can intercept mouse clicks
  1413.     if (WEGetSelectedObject(&hObjectDesc, hWE) == noErr)
  1414.     {
  1415.         if (_WEOffsetInRange(offset, edge, pWE->selStart, pWE->selEnd))
  1416.         {
  1417.             if (_WEClickObject(mouseLoc, modifiers + isMultipleClick, clickTime, hObjectDesc))
  1418.             {
  1419.                     goto cleanup;
  1420.             }
  1421.         }
  1422.     }
  1423. #endif
  1424.  
  1425.     if ((modifiers & shiftKey) == 0)
  1426.     {
  1427.  
  1428.         // is this click part of a sequence or is it a single click?
  1429.         if (isMultipleClick)
  1430.         {
  1431.             pWE->clickCount++;
  1432.  
  1433.             // a double (triple) click creates an anchor-word (anchor-line)
  1434.             if (pWE->clickCount > 1)
  1435.             {
  1436.                 WEFindLine(offset, edge, &pWE->anchorStart, &pWE->anchorEnd, hWE);
  1437.             }
  1438.             else
  1439.             {
  1440.                 WEFindWord(offset, edge, &pWE->anchorStart, &pWE->anchorEnd, hWE);
  1441.             }
  1442.  
  1443.             offset = pWE->anchorStart;
  1444.         }
  1445.         else
  1446.         {
  1447.             // single-click
  1448.             // if the Drag Manager is available and the click went in the selection range,
  1449.             // this click may be the beginning of a drag gesture
  1450.             if (BTST(pWE->flags, weFHasDragManager) && BTST(pWE->features, weFDragAndDrop))
  1451.             {
  1452.                 if (_WEOffsetInRange(offset, edge, pWE->selStart, pWE->selEnd))
  1453.                 {
  1454.                     if (_WEDrag(mouseLoc, modifiers, clickTime, hWE) != weNoDragErr)
  1455.                     {
  1456.                         goto cleanup;
  1457.                     }
  1458.                 }
  1459.             }
  1460.  
  1461.             pWE->clickCount = 0;
  1462.             anchor = offset;
  1463.         }
  1464.     }
  1465.     else
  1466.     {
  1467.  
  1468.         // if the shift key was down, use the old anchor offset found with the previous click
  1469.         anchor = BTST(pWE->flags, weFAnchorIsEnd) ? pWE->selEnd : pWE->selStart;
  1470.     }
  1471.  
  1472.     // set the weFMouseTracking bit while we track the mouse
  1473.     BSET(pWE->flags, weFMouseTracking);
  1474.  
  1475.     // MOUSE TRACKING LOOP
  1476.     do
  1477.     {
  1478.  
  1479.         // get text offset corresponding to mouse position
  1480.         WEPointToLongPoint(mouseLoc, &thePoint);
  1481.         offset = WEGetOffset(&thePoint, &edge, hWE);
  1482.  
  1483.         // if we're selecting words or lines, pin offset to a word or line boundary
  1484.         if (pWE->clickCount > 0)
  1485.         {
  1486.             if (pWE->clickCount > 1)
  1487.             {
  1488.                 WEFindLine(offset, edge, &rangeStart, &rangeEnd, hWE);
  1489.             }
  1490.             else
  1491.             {
  1492.                 WEFindWord(offset, edge, &rangeStart, &rangeEnd, hWE);
  1493.             }
  1494.  
  1495.             // choose the word/line boundary and the anchor that are farthest away from each other
  1496.             if (offset > pWE->anchorStart)
  1497.             {
  1498.                 anchor = pWE->anchorStart;
  1499.                 offset = rangeEnd;
  1500.             }
  1501.             else
  1502.             {
  1503.                 offset = rangeStart;
  1504.                 anchor = pWE->anchorEnd;
  1505.             }
  1506.         }
  1507.         else
  1508.         {
  1509.             // if the point is in the middle of an object, the selection should include it
  1510.             if (edge == kObjectEdge)
  1511.             {
  1512.                 offset++;
  1513.             }
  1514.         }
  1515.  
  1516.         // set the selection range from anchor point to current offset
  1517.         WESetSelection(anchor, offset, hWE);
  1518.  
  1519.         // call the click loop callback, if any
  1520.         if (pWE->clickLoop != nil)
  1521.         {
  1522.             if (!CallWEClickLoopProc(hWE, pWE->clickLoop))
  1523.             {
  1524.                 break;
  1525.             }
  1526.         }
  1527.  
  1528.         // update mouse position
  1529.         GetMouse(&mouseLoc);
  1530.  
  1531.     } while(WaitMouseUp());
  1532.  
  1533.     // clear the weFMouseTracking bit
  1534.     BCLR(pWE->flags, weFMouseTracking);
  1535.  
  1536.     // redraw the caret immediately if the selection range is empty
  1537.     if (anchor == offset)
  1538.     {
  1539.         _WEBlinkCaret(hWE);
  1540.     }
  1541.  
  1542. #if WASTE_IC_SUPPORT
  1543.     if (modifiers & cmdKey)
  1544.     {
  1545.         // command+clicking a URL tries to resolve it
  1546.         // we normally ask IC to parse the text surrounding the clicked point,
  1547.         // but if a selection already existed prior to the click, we pass
  1548.         // that to IC rather than forcing a re-parse
  1549.         if ((anchor != offset) || (anchor < urlStart) || (anchor > urlEnd))
  1550.         {
  1551.             urlStart = anchor;
  1552.             urlEnd = offset;
  1553.         }
  1554.         _WEResolveURL(modifiers, urlStart, urlEnd, hWE);
  1555.     }
  1556. #endif
  1557.  
  1558. cleanup:
  1559.     // unlock the WE record
  1560.     _WESetHandleLock((Handle) hWE, saveWELock);
  1561.  
  1562. }
  1563.  
  1564. pascal UInt16 WEGetClickCount(WEHandle hWE)
  1565. {
  1566.     return (*hWE)->clickCount;
  1567. }
  1568.